import { isFunc } from '../utils/index'

// symbol
const PlainDataSymbol = '__PlainData__'
const ListDataSymbol = '__ListData__'

class Data {
  constructor(param = Object) {
    this.fetching = false
    this.fetched = false
    this.error = null

    this._updateTime = 0

    if (typeof param === 'function') {
      this.data = new param()
    } else if (typeof param === 'object') {
      this.data = param
    } else {
      throw new TypeError('第一参数期待的值为*构造函数*或*对象*')
    }
  }

  setData(val) {
    this.data = val
    this._updateTime = Date.now()
  }

  /**
   * 获取数据是否过期
   * @returns {boolean}
   */
  getDataIsExpired() {
    return Date.now() - this._updateTime > 5 * 60 * 1000
  }
}

export class PlainData extends Data {
  constructor(param = Object) {
    super(param)
    this.__symbol__ = PlainDataSymbol
  }
}

export class ListData extends Data {
  constructor() {
    super(Array)
    this.__symbol__ = ListDataSymbol
    this.page = 1
    this.hasMore = true
  }
}

export function setListData(ctx, target, val, callback) {
  ctx.setData({
    [target]: Object.assign(
      new ListData(),
      {
        ...ctx.data[target],
        ...val
      }
    )
  }, callback)
}

/**
 * hor (high order request) data 高阶请求函数
 * @param ctx
 * @param data
 * @param fetchMethod
 * @param params
 * @param success
 * @param error
 * @param filter
 */
export function hor({ ctx, field, fetchMethod, params = {}, success, error, filter }) {
  return new Promise((resolve, reject) => {
    const _data = ctx.data[field]
    const isInstanceOfPlainData = _data.__symbol__ === PlainDataSymbol
    const isInstanceOfListData = _data.__symbol__ === ListDataSymbol

    // *****************
    // 检测参数是否合法
    // *****************
    if (!isInstanceOfPlainData && !isInstanceOfListData) {
      throw new TypeError('The data expected type if PlainData or ListData')
    }

    // *****************
    // 设置请求状态
    // *****************

    const Class = isInstanceOfPlainData ? PlainData : ListData

    ctx.setData({
      [field]: Object.assign(
        new Class(),
        { ...ctx.data[field], fetching: true, }
      )
    })

    fetchMethod(params)
      .then((res) => {
        ctx.setData({
          [field]: Object.assign(
            new Class(),
            {
              ...ctx.data[field],
              fetching: false,
              fetched: true,
              data: isFunc(filter) ? filter(res) : res
            }
          )
        }, () => { resolve(res) })

        isFunc(success) && success(res)
      })
      .catch((err) => {
        ctx.setData({
          [field]: Object.assign(
            new Class(),
            {
              ...ctx.data[field],
              fetching: false,
              error: err,
            }
          )
        }, () => { reject(err) })

        isFunc(error) && error(err)
      })
  })
}